import { getLastElement } from './util'
import track from './track'
const userAgent = require("user-agent")

export function injectError() {
  // 1 资源加载错误 2 同步js错误 被error事件捕获
  window.addEventListener('error', (event) => {
    let log = {}
    if (event.target && (event.target.src || event.target.href)) {
      log = {
        kind: 'stability',
        type: 'error',
        // 资源加载错误
        errorType: 'resourceError',
        errorFilename: event.target.src || event.target.href,
        lastElement: getLastElement(),
        title: document.title,
        url: window.location.href,
        timestamp: new Date().getTime(),
        userAgent: JSON.stringify(userAgent.parse(navigator.userAgent))
      }
      console.log('resource error', event, log)
      track.send(log)
    } else {
      log = {
        kind: 'stability',
        type: 'error',
        // js错误
        errorType: 'jsError',
        filename: event.filename,
        position: `${event.lineno}:${event.colno}`,
        stack: event.error && event.error.stack,
        message: event.message,
        // 最后一次触发交互的dom元素
        lastElement: getLastElement(),
        title: document.title,
        url: window.location.href,
        timestamp: new Date().getTime(),
        userAgent: JSON.stringify(userAgent.parse(navigator.userAgent))
      }
      console.log('js error', event, log)
      track.send(log)
    }
    // 资源加载错误只会在error捕获阶段捕获 所以这里要加true
  }, true)
  // addEventListener第三个参数 
  // false(默认, 冒泡, 从内到外) true(捕获 从外到内)
  // 对象 {once: '是否只执行一次', passive: '是否调用preventDefault', useCapture: '捕获还是冒泡'}

  // 3 promise错误 只能unhandledrejection捕获，上面的error事件捕获不到
  window.addEventListener('unhandledrejection', (event) => {
    const { reason } = event
    const message = (typeof reason === 'string') ? (reason + ': reject not catach') : reason.stack
    const log = {
      kind: 'stability',
      type: 'error',
      // promise错误
      errorType: 'promiseError',
      message: message,
      lastElement: getLastElement(),
      title: document.title,
      url: window.location.href,
      timestamp: new Date().getTime(),
      userAgent: JSON.stringify(userAgent.parse(navigator.userAgent))
    }
    console.log('unhandledrejection', event, log)
    track.send(log)
  })

  // 4 ajax错误 复写XMLHttpRequest.prototype.send 监听load error abort事件
  // 如果是axios等库 错误在响应拦截器里上报
  const XMLHttpRequest = window.XMLHttpRequest
  const oldOpen = XMLHttpRequest.prototype.open
  let logData = null
  XMLHttpRequest.prototype.open = function (method, url, async) {
    logData = { method, url, async }
    return oldOpen.apply(this, arguments)
  }
  const oldSend = XMLHttpRequest.prototype.send
  XMLHttpRequest.prototype.send = function (body) {
    if(logData) {
      const handler = (type) => (event) => {
        const log = {
          kind: 'stability',
          type: 'error',
          // ajax错误
          errorType: 'ajaxError',
          time: new Date().getTime(),
          eventType: type,
          url: logData.url,
          method: logData.method,
          status: this.status,
          statusText: this.statusText,
          response: JSON.stringify(this.response),
          body: body || '',
        }
        // 正确请求不上报
        if (this.status === 200) return
        // track.js的ajax错误 不继续触发track.send
        if(logData.url.match('/logstores')) return
        console.log('ajax error', log)
        track.send(log)
      }
      this.addEventListener('load', handler('loader'), false)
      this.addEventListener('error', handler('error'), false)
      this.addEventListener('abort', handler('abort'), false)
    }
    return oldSend.apply(this, arguments)
  }

  // 5 fetch错误 复写window.fetch方法
  const oldFetch = window.fetch
  window.fetch = function() {
    return oldFetch.apply(this, arguments).then(res => {
      if (res.status !== 200) {
        const log = {
          kind: 'stability',
          type: 'error',
          // fetch错误
          errorType: 'fetchError',
          time: new Date().getTime(),
          url: res.url,
          status: res.status,
          statusText: res.statusText,
        }
        console.log('fetch error', res, log)
        track.send(log)
      }
      return res
    }).catch(error => {
      console.log(error)
      // throw error
    })
  }

  // 6 script error 跨域错误
  // 出于安全考虑，浏览器会刻意隐藏跨域JS抛出的具体错误信息 只抛出script error
  // 添加crossorigin="anonymous"  脚本时不会向服务端发送潜在的用户身份信息(例如Cookies、HTTP 证书等)
  // 添加Access-Control-Allow-Origin CORS跨域
  // 这样addEventListener 'error' 就能捕获跨域JS抛出的具体错误信息

  // 7 vue及react错误
  // vue.config.errorHandle 里上报vue错误
  // react componentDidCatch 里上报vue错误
}